<?php

declare(strict_types=1);

/**
 * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

namespace OCA\Calendar\Tests\Unit\Service\Appointments;

use ChristophWurst\Nextcloud\Testing\TestCase;
use OCA\Calendar\Db\AppointmentConfig;
use OCA\Calendar\Service\Appointments\Interval;
use OCA\Calendar\Service\Appointments\SlotExtrapolator;
use OCP\Calendar\ICalendarQuery;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerInterface\PHPUnit\Framework\MockObject\MockObject;

class SlotExtrapolatorTest extends TestCase {
	/** @var SlotExtrapolator */
	private $extrapolator;
	private MockObject|LoggerInterface $logger;

	protected function setUp(): void {
		parent::setUp();

		if (!interface_exists(ICalendarQuery::class)) {
			self::markTestIncomplete();
		}

		$this->logger = $this->createMock(LoggerInterface::class);
		$this->extrapolator = new SlotExtrapolator($this->logger);
	}

	public function testNoAvailability(): void {
		$config = new AppointmentConfig();
		$config->setLength(60 * 60);
		$config->setIncrement(15 * 60);
		$availabilityIntervals = [];

		$slots = $this->extrapolator->extrapolate($config, $availabilityIntervals);

		self::assertEmpty($slots);
	}

	/**
	 * A half our availability can't fit an hour long appointment
	 */
	public function testNoneFits(): void {
		$config = new AppointmentConfig();
		$config->setLength(60 * 60);
		$config->setIncrement(15 * 60);
		$availabilityIntervals = [
			new Interval(0, 30 * 60),
		];

		$slots = $this->extrapolator->extrapolate($config, $availabilityIntervals);

		self::assertEmpty($slots);
	}

	/**
	 * An hour long availability fits exactly one hour long appointment
	 */
	public function testExactlyOne(): void {
		$config = new AppointmentConfig();
		$config->setLength(60 * 60);
		$config->setIncrement(15 * 60);
		$availabilityIntervals = [
			new Interval(0, 60 * 60),
		];

		$slots = $this->extrapolator->extrapolate($config, $availabilityIntervals);

		self::assertCount(1, $slots);
	}

	/**
	 * An hour long availability fits exactly one hour long appointment from availability generator
	 * but use an example that could be generated by the slot extrapolator
	 * i. e. Start Time is 3601 and end time is 7201, which would be
	 * parsed to 7200 and 7201
	 */
	public function testExactlyOneButRoundedUpFromAvailability(): void {
		$config = new AppointmentConfig();
		$config->setLength(3600);
		$config->setIncrement(3600);
		$slots = [new Interval(7200, 7201)];

		$slots = $this->extrapolator->extrapolate($config, $slots);

		self::assertCount(0, $slots);
	}

	/**
	 * 1.5h available allow three hour long appointments with 15m increments
	 */
	public function testOverlaps(): void {
		$config = new AppointmentConfig();
		$config->setLength(60 * 60);
		$config->setIncrement(15 * 60);
		$availabilityIntervals = [
			new Interval(0, 90 * 60),
		];

		$slots = $this->extrapolator->extrapolate($config, $availabilityIntervals);

		self::assertCount(3, $slots);
	}

	/**
	 * More than one availability that fits an appointments means multiple slots
	 */
	public function testMultipleIntervals(): void {
		$config = new AppointmentConfig();
		$config->setLength(60 * 60);
		$config->setIncrement(15 * 60);
		$availabilityIntervals = [
			new Interval(0, 60 * 60),
			new Interval(100 * 60, 160 * 60),
		];

		$slots = $this->extrapolator->extrapolate($config, $availabilityIntervals);

		self::assertCount(2, $slots);
	}
}
